using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.Build.Shared;
using Microsoft.Build.Utilities;
using Microsoft.Build.Framework;
using System.Diagnostics;

namespace EasyPak
{
	public class PakWriter : Task
	{
        string[] excludeExtensionsList; //{ ".xgs", ".xwb", ".xsb", ".pak" };
        string[] excludeFolderList;
        string[] includeFolderList;

		string contentDirectory;
        string excludeExtensions;
        string excludeFolders;
        string includeFolders;
		string outputDirectory;
		string pakName;
		bool saveFolders = true;
        bool isEnabled = true;

        public string IsEnabled
        {
            get { return isEnabled ? "true" : "false"; }
            set { isEnabled = value != null && value.ToLower() == "true"; }
        }

        [Required]
        public string PakName
        {
            get { return pakName; }
            set { pakName = value; }
        }

        /// <summary>
        /// The output directory of the pak file
        /// </summary>
		[Required]
		public string OutDir
		{
			get { return outputDirectory; }
			set { outputDirectory = value; }
		}

        /// <summary>
        /// The directory containing the built content to merge into a pak file
        /// </summary>
		[Required]
		public string ContentDir
		{
			get { return contentDirectory; }
			set { contentDirectory = value; }
		}

        /// <summary>
        /// A list of file extensions to exclude separated by semicolons and containing the dot
        /// </summary>
		public string ExcludeExtensions
		{
			get { return excludeExtensions; }
			set { excludeExtensions = value; }
		}

        public string ExcludeFolders
        {
            get { return excludeFolders; }
            set { excludeFolders = value; }
        }
        
        public string IncludeFolders
        {
            get { return includeFolders; }
            set { includeFolders = value; }
        }


		public override bool Execute()
		{
            string currentDir = Directory.GetCurrentDirectory();
			string pakFile = Path.Combine(OutDir, PakName);
            string fullContentPath = Path.GetFullPath(ContentDir);

            if (isEnabled)
            {
                Log.LogMessage(MessageImportance.High, "-- EasyPak: Executing PakWriter task for {0}", pakName);
            }
            else
            {
                Log.LogMessage(MessageImportance.High, "-- EasyPak: PakWriter task disabled for {0}", pakName);

                if (File.Exists(pakFile))
                {
                    File.Delete(pakFile);
                    Log.LogMessage(MessageImportance.High, "-- EasyPak: Deleted existing Pak file {0}", pakFile);
                }
                return true;
            }

            
			//get all the files in our content output directory
            string[] allFiles = Directory.GetFiles(fullContentPath, "*", SearchOption.AllDirectories);
            List<string> addedFiles = new List<string>();

            if (!string.IsNullOrEmpty(excludeExtensions))
            {
                excludeExtensionsList = excludeExtensions.ToLower().Trim(new char[] { ';', ' ' }).Split(new char[] { ';' });
                for (int i = 0; i < excludeExtensionsList.Length; ++i)
                    excludeExtensionsList[i] = excludeExtensionsList[i].ToLower();

                Log.LogMessage(MessageImportance.High, "EasyPak: Excluding Extensions: {0}", excludeExtensions);
            }
            if (!string.IsNullOrEmpty(excludeFolders))
            {
                excludeFolderList = excludeFolders.ToLower().Trim(new char[] { ';', ' ' }).Split(new char[] { ';' });
                for (int i = 0; i < excludeFolderList.Length; ++i)
                    excludeFolderList[i] = excludeFolderList[i].ToLower().Replace('/', '\\');

                Log.LogMessage(MessageImportance.High, "EasyPak: Excluding folders: {0}", excludeFolders);
            }
            if (!string.IsNullOrEmpty(includeFolders))
            {
                includeFolderList = includeFolders.ToLower().Trim(new char[] { ';', ' ' }).Split(new char[] { ';' });
                for (int i = 0; i < includeFolderList.Length; ++i)
                    includeFolderList[i] = includeFolderList[i].ToLower().Replace('/', '\\');

                Log.LogMessage(MessageImportance.High, "EasyPak: Including folders: {0}", includeFolders);
            }


            //loop all the content files
            foreach (string f in allFiles)
            {
                bool includeFile = true;

                string fullFile = Path.GetFullPath(f);
                string subFilePath = fullFile.Substring(fullContentPath.Length).ToLower().TrimStart(new char[] { '\\' });
                
                //Log.LogMessage(MessageImportance.High, "EasyPak: Checking file {0}", f);
                //Log.LogMessage(MessageImportance.High, "EasyPak: Full file {0}", fullFile);
                //Log.LogMessage(MessageImportance.High, "EasyPak: Sub file {0}", subFilePath); 

                if (!string.IsNullOrEmpty(includeFolders))
                {
                    includeFile = false;
                    foreach (string incFolder in includeFolderList)
                    {
                        if (subFilePath.StartsWith(incFolder))
                        {
                            includeFile = true;
                            break;
                        }
                    }
                }
                else if (!string.IsNullOrEmpty(excludeFolders))
                {
                    includeFile = true;
                    foreach (string excFolder in excludeFolderList)
                    {
                        if (subFilePath.StartsWith(excFolder))
                        {
                            includeFile = false;
                            break;
                        }
                    }
                }

                foreach (string ext in excludeExtensionsList)
                {
                    if (subFilePath.EndsWith(ext))
                    {
                        includeFile = false;
                        break;
                    }
                }

                if (includeFile)
                {
                    addedFiles.Add(f);
                }
            }

			//make sure the output directory actually exists
			if (!Directory.Exists(OutDir))
				Directory.CreateDirectory(OutDir);

            //we're figuring out whether or not to make the pak file
			bool needToCreatePak = true;

            //if the pak file exists (from a previous build for example), see if any content has changed
            if (File.Exists(pakFile))
            {
                //get the pak file's info
                FileInfo pakInfo = new FileInfo(pakFile);
                DateTime pakTimeLast = pakInfo.LastWriteTimeUtc;

                foreach (string f in addedFiles)
                {
                    try
                    {
                        //get the content info
                        FileInfo info = new FileInfo(f);

                        //if the content has a newer write time than the pak, we need to continue with the build
                        if (info.LastWriteTimeUtc.CompareTo(pakTimeLast) == 1)
                        {
                            needToCreatePak = true;
                        }
                    }
                    catch { }
                }

                //delete the pak file
                if (needToCreatePak)
                    File.Delete(pakFile);
            }
            else
            {
                needToCreatePak = true;
            }


            //bool needToRepackageXap = true;
            if (needToCreatePak)
            {
                //needToRepackageXap = true;
                if (File.Exists(pakFile))
                {
                    File.Delete(pakFile);
                }

                //create the pak file
                PakFile pak = new PakFile(pakFile);

                //move to the content directory to add the files
                Directory.SetCurrentDirectory(fullContentPath);

                Log.LogMessage(MessageImportance.High, "EasyPak: Adding files to {0}", Path.GetFileName(pakFile));

                //add all requested files to the zip
                foreach (string file in addedFiles)
                {
                    Log.LogMessage(MessageImportance.High, "\t{0}", file);

                    //make sure we have a full path
                    string fullPathFile = Path.GetFullPath(file);
                    bool keepFolder = saveFolders;


                    AddFileToPak(pak, fullPathFile.Remove(0, fullContentPath.Length));
                }

                Log.LogMessage(MessageImportance.High, "EasyPak: Saving {0}", PakName);

                //save it up
                pak.Save();
            }
            else
            {
                Log.LogMessage(MessageImportance.High, "EasyPak: {0} already up to date", PakName);
            }

            foreach (string f in addedFiles)
            {
                File.Delete(f);
            }

			//done
			return true;
		}

		void AddFileToPak(PakFile file, string path)
		{
			file.AddFile(path);
		}
	}
}
